home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume9 / uemacs3.8b / part12 < prev    next >
Encoding:
Internet Message Format  |  1987-03-16  |  32.7 KB

  1. Subject:  v09i044:  MicroEMACS, version 3.8b, Part12/14
  2. Newsgroups: mod.sources
  3. Approved: rs@mirror.TMC.COM
  4.  
  5. Submitted by: ihnp4!itivax!duncan!lawrence (Daniel Lawrence)
  6. Mod.sources: Volume 9, Issue 44
  7. Archive-name: uemacs3.8b/Part12
  8.  
  9. #! /bin/sh
  10. # This is a shell archive.  Remove anything before this line,
  11. # then unpack it by saving it in a file and typing "sh file".
  12. # If this archive is complete, you will see the message:
  13. #        "End of archive 12 (of 14)."
  14. # Contents:  search.c
  15. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  16. echo shar: Extracting \"search.c\" \(30773 characters\)
  17. if test -f search.c ; then 
  18.   echo shar: Will not over-write existing file \"search.c\"
  19. else
  20. sed "s/^X//" >search.c <<'END_OF_search.c'
  21. X/*
  22. X * The functions in this file implement commands that search in the forward
  23. X * and backward directions.  There are no special characters in the search
  24. X * strings.  Probably should have a regular expression search, or something
  25. X * like that.
  26. X *
  27. X * Aug. 1986 John M. Gamble:
  28. X *    Made forward and reverse search use the same scan routine.
  29. X *
  30. X *    Added a limited number of regular expressions - 'any',
  31. X *    'character class', 'closure', 'beginning of line', and
  32. X *    'end of line'.
  33. X *
  34. X *    Replacement metacharacters will have to wait for a re-write of
  35. X *    the replaces function, and a new variation of ldelete().
  36. X *
  37. X *    For those curious as to my references, i made use of
  38. X *    Kernighan & Plauger's "Software Tools."
  39. X *    I deliberately did not look at any published grep or editor
  40. X *    source (aside from this one) for inspiration.  I did make use of
  41. X *    Allen Hollub's bitmap routines as published in Doctor Dobb's Journal,
  42. X *    June, 1985 and modified them for the limited needs of character class
  43. X *    matching.  Any inefficiences, bugs, stupid coding examples, etc.,
  44. X *    are therefore my own responsibility.
  45. X */
  46. X
  47. X#include        <stdio.h>
  48. X#include    "estruct.h"
  49. X#include        "edef.h"
  50. X#include    "esearch.h"
  51. X
  52. X/*
  53. X * Reversed pattern array.
  54. X */
  55. Xchar    tap[NPAT];
  56. X
  57. X#if    MAGIC
  58. X/*
  59. X * The variable magical determines if there are actual
  60. X * metacharacters in the string - if not, then we don't
  61. X * have to use the slower MAGIC mode search functions.
  62. X *
  63. X * The variable mclen holds the length of the matched
  64. X * string - used by the replace functions.
  65. X *
  66. X * The arrays mcpat and tapcm hold the MC and reversed
  67. X * MC search structures.
  68. X */
  69. Xshort int    magical = FALSE;
  70. Xint        mclen = 0;
  71. XMC        mcpat[NPAT];
  72. XMC        tapcm[NPAT];
  73. X#endif
  74. X
  75. X/*
  76. X * forwsearch -- Search forward.  Get a search string from the user, and
  77. X *    search, beginning at ".", for the string.  If found, reset the "."
  78. X *    to be just after the match string, and (perhaps) repaint the display.
  79. X */
  80. X
  81. Xforwsearch(f, n)
  82. X{
  83. X    register int status = TRUE;
  84. X
  85. X    /* Resolve the repeat count.
  86. X     */
  87. X    if (n == 0)
  88. X        n = 1;
  89. X
  90. X    /* If n is negative, search backwards.
  91. X     * Otherwise proceed by asking for the search string.
  92. X     */
  93. X    if (n < 0)
  94. X        status = backsearch(f, -n);
  95. X
  96. X    /* Ask the user for the text of a pattern.  If the
  97. X     * response is TRUE (responses other than FALSE are
  98. X     * possible), search for the pattern.
  99. X     */
  100. X    else if ((status = readpattern("Search", &pat[0], TRUE)) == TRUE)
  101. X    {
  102. X        do
  103. X        {
  104. X#if    MAGIC
  105. X            if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  106. X                status = mcscanner(&mcpat[0], FORWARD, PTEND);
  107. X            else
  108. X#endif
  109. X                status = scanner(&pat[0], FORWARD, PTEND);
  110. X        } while ((--n > 0) && status);
  111. X
  112. X        /* ...and complain if not there.
  113. X         */
  114. X        if (status == FALSE)
  115. X            mlwrite("Not found");
  116. X    }
  117. X    return(status);
  118. X}
  119. X
  120. X/*
  121. X * forwhunt -- Search forward for a previously acquired search string,
  122. X *    beginning at ".".  If found, reset the "." to be just after
  123. X *    the match string, and (perhaps) repaint the display.
  124. X */
  125. X
  126. Xforwhunt(f, n)
  127. X{
  128. X    register int status = TRUE;
  129. X
  130. X    /* Resolve the repeat count.
  131. X     */
  132. X    if (n == 0)
  133. X        n = 1;
  134. X    else if (n < 0)        /* search backwards */
  135. X        return(backhunt(f, -n));
  136. X
  137. X    /* Make sure a pattern exists, or that we didn't switch
  138. X     * into MAGIC mode until after we entered the pattern.
  139. X     */
  140. X    if (pat[0] == '\0')
  141. X    {
  142. X        mlwrite("No pattern set");
  143. X        return FALSE;
  144. X    }
  145. X#if    MAGIC
  146. X    if ((curwp->w_bufp->b_mode & MDMAGIC) != 0 &&
  147. X         mcpat[0].mc_type == MCNIL)
  148. X    {
  149. X        if (!mcstr())
  150. X            return FALSE;
  151. X    }
  152. X#endif
  153. X
  154. X    /* Search for the pattern...
  155. X     */
  156. X    do
  157. X    {
  158. X#if    MAGIC
  159. X        if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  160. X            status = mcscanner(&mcpat[0], FORWARD, PTEND);
  161. X        else
  162. X#endif
  163. X            status = scanner(&pat[0], FORWARD, PTEND);
  164. X    } while ((--n > 0) && status);
  165. X
  166. X    /* ...and complain if not there.
  167. X     */
  168. X    if (status == FALSE)
  169. X        mlwrite("Not found");
  170. X
  171. X    return(status);
  172. X}
  173. X
  174. X/*
  175. X * backsearch -- Reverse search.  Get a search string from the user, and
  176. X *    search, starting at "." and proceeding toward the front of the buffer.
  177. X *    If found "." is left pointing at the first character of the pattern
  178. X *    (the last character that was matched).
  179. X */
  180. Xbacksearch(f, n)
  181. X{
  182. X    register int status = TRUE;
  183. X
  184. X    /* Resolve null and negative arguments.
  185. X     */
  186. X    if (n == 0)
  187. X        n = 1;
  188. X
  189. X    /* If n is negative, search forwards.
  190. X     * Otherwise proceed by asking for the search string.
  191. X     */
  192. X    if (n < 0)
  193. X        status = forwsearch(f, -n);
  194. X
  195. X    /* Ask the user for the text of a pattern.  If the
  196. X     * response is TRUE (responses other than FALSE are
  197. X     * possible), search for the pattern.
  198. X     */
  199. X    else if ((status = readpattern("Reverse search", &pat[0], TRUE)) == TRUE)
  200. X    {
  201. X        do
  202. X        {
  203. X#if    MAGIC
  204. X            if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  205. X                status = mcscanner(&tapcm[0], REVERSE, PTBEG);
  206. X            else
  207. X#endif
  208. X                status = scanner(&tap[0], REVERSE, PTBEG);
  209. X        } while ((--n > 0) && status);
  210. X
  211. X        /* ...and complain if not there.
  212. X         */
  213. X        if (status == FALSE)
  214. X            mlwrite("Not found");
  215. X    }
  216. X    return(status);
  217. X}
  218. X
  219. X/*
  220. X * backhunt -- Reverse search for a previously acquired search string,
  221. X *    starting at "." and proceeding toward the front of the buffer.
  222. X *    If found "." is left pointing at the first character of the pattern
  223. X *    (the last character that was matched).
  224. X */
  225. Xbackhunt(f, n)
  226. X{
  227. X    register int    status = TRUE;
  228. X
  229. X    /* Resolve null and negative arguments.
  230. X     */
  231. X    if (n == 0)
  232. X        n = 1;
  233. X    else if (n < 0)
  234. X        return(forwhunt(f, -n));
  235. X
  236. X    /* Make sure a pattern exists, or that we didn't switch
  237. X     * into MAGIC mode until after we entered the pattern.
  238. X     */
  239. X    if (tap[0] == '\0')
  240. X    {
  241. X        mlwrite("No pattern set");
  242. X        return FALSE;
  243. X    }
  244. X#if    MAGIC
  245. X    if ((curwp->w_bufp->b_mode & MDMAGIC) != 0 &&
  246. X         tapcm[0].mc_type == MCNIL)
  247. X    {
  248. X        if (!mcstr())
  249. X            return FALSE;
  250. X    }
  251. X#endif
  252. X
  253. X    /* Go search for it...
  254. X     */
  255. X    do
  256. X    {
  257. X#if    MAGIC
  258. X        if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  259. X            status = mcscanner(&tapcm[0], REVERSE, PTBEG);
  260. X        else
  261. X#endif
  262. X            status = scanner(&tap[0], REVERSE, PTBEG);
  263. X    } while ((--n > 0) && status);
  264. X
  265. X    /* ...and complain if not there.
  266. X     */
  267. X    if (status == FALSE)
  268. X        mlwrite("Not found");
  269. X
  270. X    return(status);
  271. X}
  272. X
  273. X#if    MAGIC
  274. X/*
  275. X * mcscanner -- Search for a meta-pattern in either direction.
  276. X */
  277. Xint    mcscanner(mcpatrn, direct, beg_or_end)
  278. XMC    *mcpatrn;        /* pointer into pattern */
  279. Xint    direct;        /* which way to go.*/
  280. Xint    beg_or_end;    /* put point at beginning or end of pattern.*/
  281. X{
  282. X    register LINE *lastline;    /* last line position during scan */
  283. X    register int lastoff;        /* position within last line */
  284. X    LINE *curline;            /* current line during scan */
  285. X    int curoff;            /* position within current line */
  286. X    int c;                /* (dummy) char at current position */
  287. X
  288. X    /* If we are going in reverse, then the 'end' is actually
  289. X     * the beginning of the pattern.  Toggle it.
  290. X     */
  291. X    beg_or_end ^= direct;
  292. X
  293. X    /* Setup local scan pointers to global ".".
  294. X     */
  295. X    curline = curwp->w_dotp;
  296. X    curoff  = curwp->w_doto;
  297. X
  298. X    /* Scan each character until we hit the head link record.
  299. X     */
  300. X    while (!boundry(curline, curoff, direct))
  301. X    {
  302. X        /* Save the current position in case we need to
  303. X         * restore it on a match, and initialize mclen to
  304. X         * zero in case we are doing a search for replacement.
  305. X         */
  306. X        lastline = curline;
  307. X        lastoff = curoff;
  308. X        mclen = 0;
  309. X
  310. X        if (amatch(mcpatrn, direct, &curline, &curoff))
  311. X        {
  312. X            /* A SUCCESSFULL MATCH!!!
  313. X             * reset the global "." pointers.
  314. X             */
  315. X            if (beg_or_end == PTEND)    /* at end of string */
  316. X            {
  317. X                curwp->w_dotp = curline;
  318. X                curwp->w_doto = curoff;
  319. X            }
  320. X            else        /* at beginning of string */
  321. X            {
  322. X                curwp->w_dotp = lastline;
  323. X                curwp->w_doto = lastoff;
  324. X            }
  325. X
  326. X            curwp->w_flag |= WFMOVE; /* flag that we have moved */
  327. X            return TRUE;
  328. X        }
  329. X
  330. X        /* Advance the cursor.
  331. X         */
  332. X        c = nextch(&curline, &curoff, direct);
  333. X    }
  334. X
  335. X    return FALSE;    /* We could not find a match.*/
  336. X}
  337. X
  338. X/*
  339. X * amatch -- Search for a meta-pattern in either direction.  Based on the
  340. X *    recursive routine amatch() (for "anchored match") in
  341. X *    Kernighan & Plauger's "Software Tools".
  342. X */
  343. Xstatic int    amatch(mcptr, direct, pcwline, pcwoff)
  344. Xregister MC    *mcptr;    /* string to scan for */
  345. Xint        direct;        /* which way to go.*/
  346. XLINE        **pcwline;    /* current line during scan */
  347. Xint        *pcwoff;    /* position within current line */
  348. X{
  349. X    register int c;            /* character at current position */
  350. X    LINE *curline;            /* current line during scan */
  351. X    int curoff;            /* position within current line */
  352. X    int nchars;
  353. X
  354. X    /* Set up local scan pointers to ".", and get
  355. X     * the current character.  Then loop around
  356. X     * the pattern pointer until success or failure.
  357. X     */
  358. X    curline = *pcwline;
  359. X    curoff = *pcwoff;
  360. X
  361. X    /* The beginning-of-line and end-of-line metacharacters
  362. X     * do not compare against characters, they compare
  363. X     * against positions.
  364. X     * BOL is guaranteed to be at the start of the pattern
  365. X     * for forward searches, and at the end of the pattern
  366. X     * for reverse searches.  The reverse is true for EOL.
  367. X     * So, for a start, we check for them on entry.
  368. X     */
  369. X    if (mcptr->mc_type == BOL)
  370. X    {
  371. X        if (curoff != 0)
  372. X            return FALSE;
  373. X        mcptr++;
  374. X    }
  375. X
  376. X    if (mcptr->mc_type == EOL)
  377. X    {
  378. X        if (curoff != llength(curline))
  379. X            return FALSE;
  380. X        mcptr++;
  381. X    }
  382. X
  383. X    while (mcptr->mc_type != MCNIL)
  384. X    {
  385. X        c = nextch(&curline, &curoff, direct);
  386. X
  387. X        if (mcptr->mc_type & CLOSURE)
  388. X        {
  389. X            /* Try to match as many characters as possible
  390. X             * against the current meta-character.  A
  391. X             * newline never matches a closure.
  392. X             */
  393. X            nchars = 0;
  394. X            while (c != '\n' && mceq(c, mcptr))
  395. X            {
  396. X                c = nextch(&curline, &curoff, direct);
  397. X                nchars++;
  398. X            }
  399. X
  400. X            /* We are now at the character that made us
  401. X             * fail.  Try to match the rest of the pattern.
  402. X             * Shrink the closure by one for each failure.
  403. X             * Since closure matches *zero* or more occurences
  404. X             * of a pattern, a match may start even if the
  405. X             * previous loop matched no characters.
  406. X             */
  407. X            mcptr++;
  408. X
  409. X            for (;;)
  410. X            {
  411. X                c = nextch(&curline, &curoff, direct ^ REVERSE);
  412. X
  413. X                if (amatch(mcptr, direct, &curline, &curoff))
  414. X                {
  415. X                    mclen += nchars;
  416. X                    goto success;
  417. X                }
  418. X
  419. X                if (nchars-- == 0)
  420. X                    return FALSE;
  421. X            }
  422. X        }
  423. X        else            /* Not closure.*/
  424. X        {
  425. X            /* The only way we'd get a BOL metacharacter
  426. X             * at this point is at the end of the reversed pattern.
  427. X             * The only way we'd get an EOL metacharacter
  428. X             * here is at the end of a regular pattern.
  429. X             * So if we match one or the other, and are at
  430. X             * the appropriate position, we are guaranteed success
  431. X             * (since the next pattern character has to be MCNIL).
  432. X             * Before we report success, however, we back up by
  433. X             * one character, so as to leave the cursor in the
  434. X             * correct position.  For example, a search for ")$"
  435. X             * will leave the cursor at the end of the line, while
  436. X             * a search for ")<NL>" will leave the cursor at the
  437. X             * beginning of the next line.  This follows the
  438. X             * notion that the meta-character '$' (and likewise
  439. X             * '^') match positions, not characters.
  440. X             */
  441. X            if (mcptr->mc_type == BOL)
  442. X                if (curoff == llength(curline))
  443. X                {
  444. X                    c = nextch(&curline, &curoff,
  445. X                           direct ^ REVERSE);
  446. X                    goto success;
  447. X                }
  448. X                else
  449. X                    return FALSE;
  450. X
  451. X            if (mcptr->mc_type == EOL)
  452. X                if (curoff == 0)
  453. X                {
  454. X                    c = nextch(&curline, &curoff,
  455. X                           direct ^ REVERSE);
  456. X                    goto success;
  457. X                }
  458. X                else
  459. X                    return FALSE;
  460. X
  461. X            /* Neither BOL nor EOL, so go through
  462. X             * the meta-character equal function.
  463. X             */
  464. X            if (!mceq(c, mcptr))
  465. X                return FALSE;
  466. X        }
  467. X
  468. X        /* Increment the length counter and
  469. X         * advance the pattern pointer.
  470. X         */
  471. X        mclen++;
  472. X        mcptr++;
  473. X    }            /* End of mcptr loop.*/
  474. X
  475. X    /* A SUCCESSFULL MATCH!!!
  476. X     * Reset the "." pointers.
  477. X     */
  478. Xsuccess:
  479. X    *pcwline = curline;
  480. X    *pcwoff  = curoff;
  481. X
  482. X    return TRUE;
  483. X}
  484. X#endif
  485. X
  486. X/*
  487. X * scanner -- Search for a pattern in either direction.
  488. X */
  489. Xint    scanner(patrn, direct, beg_or_end)
  490. Xchar    *patrn;        /* string to scan for */
  491. Xint    direct;        /* which way to go.*/
  492. Xint    beg_or_end;    /* put point at beginning or end of pattern.*/
  493. X{
  494. X    register int c;            /* character at current position */
  495. X    register char *patptr;        /* pointer into pattern */
  496. X    register LINE *lastline;    /* last line position during scan */
  497. X    register int lastoff;        /* position within last line */
  498. X    LINE *curline;            /* current line during scan */
  499. X    int curoff;            /* position within current line */
  500. X    LINE *matchline;        /* current line during matching */
  501. X    int matchoff;            /* position in matching line */
  502. X
  503. X    /* If we are going in reverse, then the 'end' is actually
  504. X     * the beginning of the pattern.  Toggle it.
  505. X     */
  506. X    beg_or_end ^= direct;
  507. X
  508. X    /* Setup local scan pointers to global ".".
  509. X     */
  510. X    curline = curwp->w_dotp;
  511. X    curoff = curwp->w_doto;
  512. X
  513. X    /* Scan each character until we hit the head link record.
  514. X     */
  515. X    while (!boundry(curline, curoff, direct))
  516. X    {
  517. X        /* Save the current position in case we need to
  518. X         * restore it on a match.
  519. X         */
  520. X        lastline = curline;
  521. X        lastoff = curoff;
  522. X
  523. X        /* Get the character resolving newlines, and
  524. X         * test it against first char in pattern.
  525. X         */
  526. X        c = nextch(&curline, &curoff, direct);
  527. X
  528. X        if (eq(c, patrn[0]))    /* if we find it..*/
  529. X        {
  530. X            /* Setup match pointers.
  531. X             */
  532. X            matchline = curline;
  533. X            matchoff = curoff;
  534. X            patptr = &patrn[0];
  535. X
  536. X            /* Scan through the pattern for a match.
  537. X             */
  538. X            while (*++patptr != '\0')
  539. X            {
  540. X                c = nextch(&matchline, &matchoff, direct);
  541. X
  542. X                if (!eq(c, *patptr))
  543. X                    goto fail;
  544. X            }
  545. X
  546. X            /* A SUCCESSFULL MATCH!!!
  547. X             * reset the global "." pointers
  548. X             */
  549. X            if (beg_or_end == PTEND)    /* at end of string */
  550. X            {
  551. X                curwp->w_dotp = matchline;
  552. X                curwp->w_doto = matchoff;
  553. X            }
  554. X            else        /* at beginning of string */
  555. X            {
  556. X                curwp->w_dotp = lastline;
  557. X                curwp->w_doto = lastoff;
  558. X            }
  559. X
  560. X            curwp->w_flag |= WFMOVE; /* Flag that we have moved.*/
  561. X            return TRUE;
  562. X
  563. X        }
  564. Xfail:;            /* continue to search */
  565. X    }
  566. X
  567. X    return FALSE;    /* We could not find a match */
  568. X}
  569. X
  570. X/*
  571. X * eq -- Compare two characters.  The "bc" comes from the buffer, "pc"
  572. X *    from the pattern.  If we are not in EXACT mode, fold out the case.
  573. X */
  574. Xint    eq(bc, pc)
  575. Xregister int    bc;
  576. Xregister int    pc;
  577. X{
  578. X    if ((curwp->w_bufp->b_mode & MDEXACT) == 0)
  579. X    {
  580. X        if (islower(bc))
  581. X            bc ^= DIFCASE;
  582. X
  583. X        if (islower(pc))
  584. X            pc ^= DIFCASE;
  585. X    }
  586. X
  587. X    return (bc == pc);
  588. X}
  589. X
  590. X/*
  591. X * readpattern -- Read a pattern.  Stash it in apat.  If it is the
  592. X *    search string, create the reverse pattern and the magic
  593. X *    pattern, assuming we are in MAGIC mode (and defined that way).
  594. X *    Apat is not updated if the user types in an empty line.  If
  595. X *    the user typed an empty line, and there is no old pattern, it is
  596. X *    an error.  Display the old pattern, in the style of Jeff Lomicka.
  597. X *    There is some do-it-yourself control expansion.  Change to using
  598. X *    <META> to delimit the end-of-pattern to allow <NL>s in the search
  599. X *    string. 
  600. X */
  601. Xstatic int    readpattern(prompt, apat, srch)
  602. Xchar    *prompt;
  603. Xchar    apat[];
  604. Xint    srch;
  605. X{
  606. X    int status;
  607. X    char tpat[NPAT+20];
  608. X
  609. X    strcpy(tpat, prompt);    /* copy prompt to output string */
  610. X    strcat(tpat, " [");    /* build new prompt string */
  611. X    expandp(&apat[0], &tpat[strlen(tpat)], NPAT/2);    /* add old pattern */
  612. X    strcat(tpat, "]<META>: ");
  613. X
  614. X    /* Read a pattern.  Either we get one,
  615. X     * or we just get the META charater, and use the previous pattern.
  616. X     * Then, if it's the search string, make a reversed pattern.
  617. X     * *Then*, make the meta-pattern, if we are defined that way.
  618. X     */
  619. X    if ((status = mlreplyt(tpat, tpat, NPAT, metac)) == TRUE)
  620. X    {
  621. X        strcpy(apat, tpat);
  622. X        if (srch)    /* If we are doing the search string.*/
  623. X        {
  624. X            /* Reverse string copy.
  625. X             */
  626. X            rvstrcpy(tap, apat);
  627. X#if    MAGIC
  628. X            /* Only make the meta-pattern if in magic mode,
  629. X             * since the pattern in question might have an
  630. X             * invalid meta combination.
  631. X             */
  632. X            if ((curwp->w_bufp->b_mode & MDMAGIC) == 0)
  633. X                mcpat[0].mc_type = tapcm[0].mc_type = MCNIL;
  634. X            else
  635. X                status = mcstr();
  636. X#endif
  637. X        }
  638. X    }
  639. X    else if (status == FALSE && apat[0] != 0)    /* Old one */
  640. X        status = TRUE;
  641. X
  642. X    return(status);
  643. X}
  644. X
  645. X/*
  646. X * rvstrcpy -- Reverse string copy.
  647. X */
  648. Xrvstrcpy(rvstr, str)
  649. Xregister char    *rvstr, *str;
  650. X{
  651. X    register int i;
  652. X
  653. X    str += (i = strlen(str));
  654. X
  655. X    while (i-- > 0)
  656. X        *rvstr++ = *--str;
  657. X
  658. X    *rvstr = '\0';
  659. X}
  660. X
  661. X/*
  662. X * sreplace -- Search and replace.
  663. X */
  664. Xsreplace(f, n)
  665. Xint f;        /* default flag */
  666. Xint n;        /* # of repetitions wanted */
  667. X{
  668. X    return(replaces(FALSE, f, n));
  669. X}
  670. X
  671. X/*
  672. X * qreplace -- search and replace with query.
  673. X */
  674. Xqreplace(f, n)
  675. Xint f;        /* default flag */
  676. Xint n;        /* # of repetitions wanted */
  677. X{
  678. X    return(replaces(TRUE, f, n));
  679. X}
  680. X
  681. X/*
  682. X * replaces -- Search for a string and replace it with another
  683. X *    string.  Query might be enabled (according to kind).
  684. X */
  685. Xstatic int    replaces(kind, f, n)
  686. Xint    kind;    /* Query enabled flag */
  687. Xint    f;    /* default flag */
  688. Xint    n;    /* # of repetitions wanted */
  689. X{
  690. X    register int i;        /* loop index */
  691. X    register int status;    /* success flag on pattern inputs */
  692. X    register int slength,
  693. X             rlength;    /* length of search and replace strings */
  694. X    register int numsub;    /* number of substitutions */
  695. X    register int nummatch;    /* number of found matches */
  696. X    int nlflag;        /* last char of search string a <NL>? */
  697. X    int nlrepl;        /* was a replace done on the last line? */
  698. X    char tmpc;        /* temporary character */
  699. X    char c;            /* input char for query */
  700. X    char tpat[NPAT];    /* temporary to hold search pattern */
  701. X    LINE *origline;        /* original "." position */
  702. X    int origoff;        /* and offset (for . query option) */
  703. X    LINE *lastline;        /* position of last replace and */
  704. X    int lastoff;        /* offset (for 'u' query option) */
  705. X
  706. X    if (curbp->b_mode & MDVIEW)    /* don't allow this command if    */
  707. X        return(rdonly());    /* we are in read only mode    */
  708. X
  709. X    /* Check for negative repetitions.
  710. X     */
  711. X    if (f && n < 0)
  712. X        return(FALSE);
  713. X
  714. X    /* Ask the user for the text of a pattern.
  715. X     */
  716. X    if ((status = readpattern(
  717. X        (kind == FALSE ? "Replace" : "Query replace"), &pat[0], TRUE))
  718. X                                != TRUE)
  719. X        return(status);
  720. X
  721. X    /* Ask for the replacement string.
  722. X     */
  723. X    if ((status = readpattern("with", &rpat[0], FALSE)) == ABORT)
  724. X        return(status);
  725. X
  726. X    /* Find the lengths of the strings.
  727. X     */
  728. X    slength = strlen(&pat[0]);
  729. X    rlength = strlen(&rpat[0]);
  730. X
  731. X    /* Set up flags so we can make sure not to do a recursive
  732. X     * replace on the last line.
  733. X     */
  734. X    nlflag = (pat[slength - 1] == '\n');
  735. X    nlrepl = FALSE;
  736. X
  737. X    if (kind)
  738. X    {
  739. X        /* Build query replace question string.
  740. X         */
  741. X        strcpy(tpat, "Replace '");
  742. X        expandp(&pat[0], &tpat[strlen(tpat)], NPAT/3);
  743. X        strcat(tpat, "' with '");
  744. X        expandp(&rpat[0], &tpat[strlen(tpat)], NPAT/3);
  745. X        strcat(tpat, "'? ");
  746. X
  747. X        /* Initialize last replaced pointers.
  748. X         */
  749. X        lastline = NULL;
  750. X        lastoff = 0;
  751. X    }
  752. X
  753. X    /* Save original . position, init the number of matches and
  754. X     * substitutions, and scan through the file.
  755. X     */
  756. X    origline = curwp->w_dotp;
  757. X    origoff = curwp->w_doto;
  758. X    numsub = 0;
  759. X    nummatch = 0;
  760. X
  761. X    while ( (f == FALSE || n > nummatch) &&
  762. X        (nlflag == FALSE || nlrepl == FALSE) )
  763. X    {
  764. X        /* Search for the pattern.
  765. X         * If we search with a regular expression, also save
  766. X         * the true length of matched string.
  767. X         */
  768. X#if    MAGIC
  769. X        if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  770. X        {
  771. X            if (!mcscanner(&mcpat[0], FORWARD, PTBEG))
  772. X                break;
  773. X            slength = mclen;
  774. X        }
  775. X        else
  776. X#endif
  777. X            if (!scanner(&pat[0], FORWARD, PTBEG))
  778. X                break;        /* all done */
  779. X
  780. X        ++nummatch;    /* Increment # of matches */
  781. X
  782. X        /* Check if we are on the last line.
  783. X         */
  784. X        nlrepl = (lforw(curwp->w_dotp) == curwp->w_bufp->b_linep);
  785. X
  786. X        /* Check for query.
  787. X         */
  788. X        if (kind)
  789. X        {
  790. X            /* Get the query.
  791. X             */
  792. Xpprompt:        mlwrite(&tpat[0], &pat[0], &rpat[0]);
  793. Xqprompt:
  794. X            update();  /* show the proposed place to change */
  795. X            c = tgetc();            /* and input */
  796. X            mlwrite("");            /* and clear it */
  797. X
  798. X            /* And respond appropriately.
  799. X             */
  800. X            switch (c)
  801. X            {
  802. X                case 'y':    /* yes, substitute */
  803. X                case ' ':
  804. X                    break;
  805. X
  806. X                case 'n':    /* no, onword */
  807. X                    forwchar(FALSE, 1);
  808. X                    continue;
  809. X
  810. X                case '!':    /* yes/stop asking */
  811. X                    kind = FALSE;
  812. X                    break;
  813. X
  814. X                case 'u':    /* undo last and re-prompt */
  815. X
  816. X                    /* Restore old position.
  817. X                     */
  818. X                    if (lastline == NULL)
  819. X                    {
  820. X                        /* There is nothing to undo.
  821. X                         */
  822. X                        TTbeep();
  823. X                        goto qprompt;
  824. X                    }
  825. X                    curwp->w_dotp = lastline;
  826. X                    curwp->w_doto = lastoff;
  827. X                    lastline = NULL;
  828. X                    lastoff = 0;
  829. X
  830. X                    /* Delete the new string.
  831. X                     */
  832. X                    backchar(FALSE, rlength);
  833. X                    if (!ldelete(rlength, FALSE))
  834. X                    {
  835. X                        mlwrite("%%ERROR while deleting");
  836. X                        return(FALSE);
  837. X                    }
  838. X
  839. X                    /* And put in the old one.
  840. X                     */
  841. X                    for (i = 0; i < slength; i++)
  842. X                    {
  843. X                        tmpc = pat[i];
  844. X                        status = (tmpc == '\n'?
  845. X                            lnewline():
  846. X                            linsert(1, tmpc));
  847. X
  848. X                        /* Insertion error?
  849. X                         */
  850. X                        if (!status)
  851. X                        {
  852. X                            mlwrite("%%Out of memory while inserting");
  853. X                            return(FALSE);
  854. X                        }
  855. X                    }
  856. X
  857. X                    /* Record one less substitution,
  858. X                     * backup, and reprompt.
  859. X                     */
  860. X                    --numsub;
  861. X                    backchar(FALSE, slength);
  862. X                    goto pprompt;
  863. X
  864. X                case '.':    /* abort! and return */
  865. X                    /* restore old position */
  866. X                    curwp->w_dotp = origline;
  867. X                    curwp->w_doto = origoff;
  868. X                    curwp->w_flag |= WFMOVE;
  869. X
  870. X                case BELL:    /* abort! and stay */
  871. X                    mlwrite("Aborted!");
  872. X                    return(FALSE);
  873. X
  874. X                default:    /* bitch and beep */
  875. X                    TTbeep();
  876. X
  877. X                case '?':    /* help me */
  878. X                    mlwrite(
  879. X"(Y)es, (N)o, (!)Do rest, (U)ndo last, (^G)Abort, (.)Abort back, (?)Help: ");
  880. X                    goto qprompt;
  881. X
  882. X            }    /* end of switch */
  883. X        }    /* end of "if kind" */
  884. X
  885. X        /*
  886. X         * Delete the sucker.
  887. X         */
  888. X        if (!ldelete(slength, FALSE))
  889. X        {
  890. X            mlwrite("%%ERROR while deleteing");
  891. X            return(FALSE);
  892. X        }
  893. X
  894. X        /* And insert its replacement.
  895. X         */
  896. X        for (i = 0; i < rlength; i++)
  897. X        {
  898. X            tmpc = rpat[i];
  899. X            status = (tmpc == '\n'? lnewline(): linsert(1, tmpc));
  900. X
  901. X            /* Insertion error?
  902. X             */
  903. X            if (!status)
  904. X            {
  905. X                mlwrite("%%Out of memory while inserting");
  906. X                return(FALSE);
  907. X            }
  908. X        }
  909. X
  910. X        /* Save where we are if we might undo this....
  911. X         */
  912. X        if (kind)
  913. X        {
  914. X            lastline = curwp->w_dotp;
  915. X            lastoff = curwp->w_doto;
  916. X        }
  917. X
  918. X        numsub++;    /* increment # of substitutions */
  919. X    }
  920. X
  921. X    /* And report the results.
  922. X     */
  923. X    mlwrite("%d substitutions", numsub);
  924. X    return(TRUE);
  925. X}
  926. X
  927. X/*
  928. X * expandp -- Expand control key sequences for output.
  929. X */
  930. Xexpandp(srcstr, deststr, maxlength)
  931. Xchar *srcstr;    /* string to expand */
  932. Xchar *deststr;    /* destination of expanded string */
  933. Xint maxlength;    /* maximum chars in destination */
  934. X{
  935. X    char c;        /* current char to translate */
  936. X
  937. X    /* Scan through the string.
  938. X     */
  939. X    while ((c = *srcstr++) != 0)
  940. X    {
  941. X        if (c == '\n')        /* it's a newline */
  942. X        {
  943. X            *deststr++ = '<';
  944. X            *deststr++ = 'N';
  945. X            *deststr++ = 'L';
  946. X            *deststr++ = '>';
  947. X            maxlength -= 4;
  948. X        }
  949. X        else if (c < 0x20 || c == 0x7f)    /* control character */
  950. X        {
  951. X            *deststr++ = '^';
  952. X            *deststr++ = c ^ 0x40;
  953. X            maxlength -= 2;
  954. X        }
  955. X        else if (c == '%')
  956. X        {
  957. X            *deststr++ = '%';
  958. X            *deststr++ = '%';
  959. X            maxlength -= 2;
  960. X        }
  961. X        else            /* any other character */
  962. X        {
  963. X            *deststr++ = c;
  964. X            maxlength--;
  965. X        }
  966. X
  967. X        /* check for maxlength */
  968. X        if (maxlength < 4)
  969. X        {
  970. X            *deststr++ = '$';
  971. X            *deststr = '\0';
  972. X            return(FALSE);
  973. X        }
  974. X    }
  975. X    *deststr = '\0';
  976. X    return(TRUE);
  977. X}
  978. X
  979. X/*
  980. X * boundry -- Return information depending on whether we may search no
  981. X *    further.  Beginning of file and end of file are the obvious
  982. X *    cases, but we may want to add further optional boundry restrictions
  983. X *    in future, a' la VMS EDT.  At the moment, just return TRUE or
  984. X *    FALSE depending on if a boundry is hit (ouch).
  985. X */
  986. Xint    boundry(curline, curoff, dir)
  987. XLINE    *curline;
  988. Xint    curoff, dir;
  989. X{
  990. X    register int    border;
  991. X
  992. X    if (dir == FORWARD)
  993. X    {
  994. X        border = (curoff == llength(curline)) &&
  995. X             (lforw(curline) == curbp->b_linep);
  996. X    }
  997. X    else
  998. X    {
  999. X        border = (curoff == 0) &&
  1000. X             (lback(curline) == curbp->b_linep);
  1001. X    }
  1002. X    return (border);
  1003. X}
  1004. X
  1005. X/*
  1006. X * nextch -- retrieve the next/previous character in the buffer,
  1007. X *    and advance/retreat the point.
  1008. X *    The order in which this is done is significant, and depends
  1009. X *    upon the direction of the search.  Forward searches look at
  1010. X *    the current character and move, reverse searches move and
  1011. X *    look at the character.
  1012. X */
  1013. Xstatic int    nextch(pcurline, pcuroff, dir)
  1014. XLINE    **pcurline;
  1015. Xint    *pcuroff;
  1016. Xint    dir;
  1017. X{
  1018. X    register LINE    *curline;
  1019. X    register int    curoff;
  1020. X    register int    c;
  1021. X
  1022. X    curline = *pcurline;
  1023. X    curoff = *pcuroff;
  1024. X
  1025. X    if (dir == FORWARD)
  1026. X    {
  1027. X        if (curoff == llength(curline))        /* if at EOL */
  1028. X        {
  1029. X            curline = lforw(curline);    /* skip to next line */
  1030. X            curoff = 0;
  1031. X            c = '\n';            /* and return a <NL> */
  1032. X        }
  1033. X        else
  1034. X            c = lgetc(curline, curoff++);    /* get the char */
  1035. X    }
  1036. X    else            /* Reverse.*/
  1037. X    {
  1038. X        if (curoff == 0)
  1039. X        {
  1040. X            curline = lback(curline);
  1041. X            curoff = llength(curline);
  1042. X            c = '\n';
  1043. X        }
  1044. X        else
  1045. X            c = lgetc(curline, --curoff);
  1046. X
  1047. X    }
  1048. X    *pcurline = curline;
  1049. X    *pcuroff = curoff;
  1050. X
  1051. X    return (c);
  1052. X}
  1053. X
  1054. X#if    MAGIC
  1055. X/*
  1056. X * mcstr -- Set up the 'magic' array.  The closure symbol is taken as
  1057. X *    a literal character when (1) it is the first character in the
  1058. X *    pattern, and (2) when preceded by a symbol that does not allow
  1059. X *    closure, such as a newline, beginning of line symbol, or another
  1060. X *    closure symbol.
  1061. X *
  1062. X *    Coding comment (jmg):  yes, i know i have gotos that are, strictly
  1063. X *    speaking, unnecessary.  But right now we are so cramped for
  1064. X *    code space that i will grab what i can in order to remain
  1065. X *    within the 64K limit.  C compilers actually do very little
  1066. X *    in the way of optimizing - they expect you to do that.
  1067. X */
  1068. Xstatic int    mcstr()
  1069. X{
  1070. X    MC    *mcptr, *rtpcm;
  1071. X    char    *patptr;
  1072. X     int    mj;
  1073. X     int    pchr;
  1074. X     int    status = TRUE;
  1075. X     int    does_closure = FALSE;
  1076. X
  1077. X    /* If we had metacharacters in the MC array previously,
  1078. X     * free up any bitmaps that may have been allocated.
  1079. X     */
  1080. X    if (magical)
  1081. X        freebits();
  1082. X
  1083. X    magical = FALSE;
  1084. X    mj = 0;
  1085. X    mcptr = &mcpat[0];
  1086. X    patptr = &pat[0];
  1087. X
  1088. X    while ((pchr = *patptr) && status)
  1089. X    {
  1090. X        switch (pchr)
  1091. X        {
  1092. X            case MC_CCL:
  1093. X                status = cclmake(&patptr, mcptr);
  1094. X                magical = TRUE;
  1095. X                does_closure = TRUE;
  1096. X                break;
  1097. X            case MC_BOL:
  1098. X                if (mj != 0)
  1099. X                    goto litcase;
  1100. X
  1101. X                mcptr->mc_type = BOL;
  1102. X                magical = TRUE;
  1103. X                does_closure = FALSE;
  1104. X                break;
  1105. X            case MC_EOL:
  1106. X                if (*(patptr + 1) != '\0')
  1107. X                    goto litcase;
  1108. X
  1109. X                mcptr->mc_type = EOL;
  1110. X                magical = TRUE;
  1111. X                does_closure = FALSE;
  1112. X                break;
  1113. X            case MC_ANY:
  1114. X                mcptr->mc_type = ANY;
  1115. X                magical = TRUE;
  1116. X                does_closure = TRUE;
  1117. X                break;
  1118. X            case MC_CLOSURE:
  1119. X                /* Does the closure symbol mean closure here?
  1120. X                 * If so, back up to the previous element
  1121. X                 * and indicate it is enclosed.
  1122. X                 */
  1123. X                if (!does_closure)
  1124. X                    goto litcase;
  1125. X                mj--;
  1126. X                mcptr--;
  1127. X                mcptr->mc_type |= CLOSURE;
  1128. X                magical = TRUE;
  1129. X                does_closure = FALSE;
  1130. X                break;
  1131. X
  1132. X            /* Note: no break between MC_ESC case and the default.
  1133. X             */
  1134. X            case MC_ESC:
  1135. X                if (*(patptr + 1) != '\0')
  1136. X                {
  1137. X                    pchr = *++patptr;
  1138. X                    magical = TRUE;
  1139. X                }
  1140. X            default:
  1141. Xlitcase:            mcptr->mc_type = LITCHAR;
  1142. X                mcptr->u.lchar = pchr;
  1143. X                does_closure = (pchr != '\n');
  1144. X                break;
  1145. X        }        /* End of switch.*/
  1146. X        mcptr++;
  1147. X        patptr++;
  1148. X        mj++;
  1149. X    }        /* End of while.*/
  1150. X
  1151. X    /* Close off the meta-string.
  1152. X     */
  1153. X    mcptr->mc_type = MCNIL;
  1154. X
  1155. X    /* Set up the reverse array, if the status is good.  Please note the
  1156. X     * structure assignment - your compiler may not like that.
  1157. X     * If the status is not good, nil out the meta-pattern.
  1158. X     * The only way the status would be bad is from the cclmake()
  1159. X     * routine, and the bitmap for that member is guarenteed to be
  1160. X     * freed.  So we stomp a MCNIL value there, call freebits() to
  1161. X     * free any other bitmaps, and set the zeroth array to MCNIL.
  1162. X     */
  1163. X    if (status)
  1164. X    {
  1165. X        rtpcm = &tapcm[0];
  1166. X        while (--mj >= 0)
  1167. X        {
  1168. X#if    LATTICE
  1169. X            movmem(--mcptr, rtpcm++, sizeof (MC));
  1170. X#endif
  1171. X
  1172. X#if    MWC86 | AZTEC | MSC | VMS | USG | BSD | V7
  1173. X            *rtpcm++ = *--mcptr;
  1174. X#endif
  1175. X        }
  1176. X        rtpcm->mc_type = MCNIL;
  1177. X    }
  1178. X    else
  1179. X    {
  1180. X        (--mcptr)->mc_type = MCNIL;
  1181. X        freebits();
  1182. X        mcpat[0].mc_type = tapcm[0].mc_type = MCNIL;
  1183. X    }
  1184. X
  1185. X    return(status);
  1186. X}
  1187. X
  1188. X/*
  1189. X * mceq -- meta-character equality with a character.  In Kernighan & Plauger's
  1190. X *    Software Tools, this is the function omatch(), but i felt there
  1191. X *    were too many functions with the 'match' name already.
  1192. X */
  1193. Xstatic int    mceq(bc, mt)
  1194. Xint    bc;
  1195. XMC    *mt;
  1196. X{
  1197. X    register int result;
  1198. X
  1199. X    switch (mt->mc_type & MASKCL)
  1200. X    {
  1201. X        case LITCHAR:
  1202. X            result = eq(bc, mt->u.lchar);
  1203. X            break;
  1204. X
  1205. X        case ANY:
  1206. X            result = (bc != '\n');
  1207. X            break;
  1208. X
  1209. X        case CCL:
  1210. X            if (!(result = biteq(bc, mt->u.cclmap)))
  1211. X            {
  1212. X                if ((curwp->w_bufp->b_mode & MDEXACT) == 0 &&
  1213. X                    (isletter(bc)))
  1214. X                {
  1215. X                    result = biteq(CHCASE(bc), mt->u.cclmap);
  1216. X                }
  1217. X            }
  1218. X            break;
  1219. X
  1220. X        case NCCL:
  1221. X            result = !biteq(bc, mt->u.cclmap);
  1222. X
  1223. X            if ((curwp->w_bufp->b_mode & MDEXACT) == 0 &&
  1224. X                (isletter(bc)))
  1225. X            {
  1226. X                result &= !biteq(CHCASE(bc), mt->u.cclmap);
  1227. X            }
  1228. X            break;
  1229. X
  1230. X        default:
  1231. X            mlwrite("mceq: what is %d?", mt->mc_type);
  1232. X            result = FALSE;
  1233. X            break;
  1234. X
  1235. X    }    /* End of switch.*/
  1236. X
  1237. X    return (result);
  1238. X}
  1239. X
  1240. X/*
  1241. X * cclmake -- create the bitmap for the character class.
  1242. X *    ppatptr is left pointing to the end-of-character-class character,
  1243. X *    so that a loop may automatically increment with safety.
  1244. X */
  1245. Xstatic int    cclmake(ppatptr, mcptr)
  1246. Xchar    **ppatptr;
  1247. XMC    *mcptr;
  1248. X{
  1249. X    BITMAP        clearbits();
  1250. X    BITMAP        bmap;
  1251. X    register char    *patptr;
  1252. X    register int    pchr, ochr;
  1253. X
  1254. X    if ((bmap = clearbits()) == NULL)
  1255. X    {
  1256. X        mlwrite("%%Out of memory");
  1257. X        return FALSE;
  1258. X    }
  1259. X
  1260. X    mcptr->u.cclmap = bmap;
  1261. X    patptr = *ppatptr;
  1262. X
  1263. X    /*
  1264. X     * Test the initial character(s) in ccl for
  1265. X     * special cases - negate ccl, or an end ccl
  1266. X     * character as a first character.  Anything
  1267. X     * else gets set in the bitmap.
  1268. X     */
  1269. X    if (*++patptr == MC_NCCL)
  1270. X    {
  1271. X        patptr++;
  1272. X        mcptr->mc_type = NCCL;
  1273. X    }
  1274. X    else
  1275. X        mcptr->mc_type = CCL;
  1276. X
  1277. X    if ((ochr = *patptr) == MC_ECCL)
  1278. X    {
  1279. X        mlwrite("%%No characters in character class");
  1280. X        return (FALSE);
  1281. X    }
  1282. X    else
  1283. X    {
  1284. X        if (ochr == MC_ESC)
  1285. X            ochr = *++patptr;
  1286. X
  1287. X        setbit(ochr, bmap);
  1288. X        patptr++;
  1289. X    }
  1290. X
  1291. X    while (ochr != '\0' && (pchr = *patptr) != MC_ECCL)
  1292. X    {
  1293. X        switch (pchr)
  1294. X        {
  1295. X            /* Range character loses its meaning
  1296. X             * if it is the last character in
  1297. X             * the class.
  1298. X             */
  1299. X            case MC_RCCL:
  1300. X                if (*(patptr + 1) == MC_ECCL)
  1301. X                    setbit(pchr, bmap);
  1302. X                else
  1303. X                {
  1304. X                    pchr = *++patptr;
  1305. X                    while (++ochr <= pchr)
  1306. X                        setbit(ochr, bmap);
  1307. X                }
  1308. X                break;
  1309. X
  1310. X            /* Note: no break between case MC_ESC and the default.
  1311. X             */
  1312. X            case MC_ESC:
  1313. X                pchr = *++patptr;
  1314. X            default:
  1315. X                setbit(pchr, bmap);
  1316. X                break;
  1317. X        }
  1318. X        patptr++;
  1319. X        ochr = pchr;
  1320. X    }
  1321. X
  1322. X    *ppatptr = patptr;
  1323. X
  1324. X    if (ochr == '\0')
  1325. X    {
  1326. X        mlwrite("%%Character class not ended");
  1327. X        free(bmap);
  1328. X        return FALSE;
  1329. X    }
  1330. X    return TRUE;
  1331. X}
  1332. X
  1333. X/*
  1334. X * biteq -- is the character in the bitmap?
  1335. X */
  1336. Xstatic int    biteq(bc, cclmap)
  1337. Xint    bc;
  1338. XBITMAP    cclmap;
  1339. X{
  1340. X    if (bc >= HICHAR)
  1341. X        return FALSE;
  1342. X
  1343. X    return( (*(cclmap + (bc >> 3)) & BIT(bc & 7))? TRUE: FALSE );
  1344. X}
  1345. X
  1346. X/*
  1347. X * clearbits -- Allocate and zero out a CCL bitmap.
  1348. X */
  1349. Xstatic    BITMAP clearbits()
  1350. X{
  1351. X    char        *malloc();
  1352. X
  1353. X    BITMAP        cclstart, cclmap;
  1354. X    register int    j;
  1355. X
  1356. X    if ((cclmap = cclstart = (BITMAP) malloc(HIBYTE)) != NULL)
  1357. X        for (j = 0; j < HIBYTE; j++)
  1358. X            *cclmap++ = 0;
  1359. X
  1360. X    return (cclstart);
  1361. X}
  1362. X
  1363. X/*
  1364. X * freebits -- Free up any CCL bitmaps.
  1365. X */
  1366. Xstatic    freebits()
  1367. X{
  1368. X    register MC    *mcptr;
  1369. X
  1370. X    mcptr = &mcpat[0];
  1371. X
  1372. X    while (mcptr->mc_type != MCNIL)
  1373. X    {
  1374. X        if ((mcptr->mc_type & MASKCL) == CCL ||
  1375. X            (mcptr->mc_type & MASKCL) == NCCL)
  1376. X            free(mcptr->u.cclmap);
  1377. X        mcptr++;
  1378. X    }
  1379. X}
  1380. X
  1381. X/*
  1382. X * setbit -- Set a bit (ON only) in the bitmap.
  1383. X */
  1384. Xstatic    setbit(bc, cclmap)
  1385. Xint    bc;
  1386. XBITMAP    cclmap;
  1387. X{
  1388. X    if (bc < HICHAR)
  1389. X        *(cclmap + (bc >> 3)) |= BIT(bc & 7);
  1390. X}
  1391. X#endif
  1392. END_OF_search.c
  1393. if test 30773 -ne `wc -c <search.c`; then
  1394.     echo shar: \"search.c\" unpacked with wrong size!
  1395. fi
  1396. # end of overwriting check
  1397. fi
  1398. echo shar: End of archive 12 \(of 14\).
  1399. cp /dev/null ark12isdone
  1400. MISSING=""
  1401. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ; do
  1402.     if test ! -f ark${I}isdone ; then
  1403.     MISSING="${MISSING} ${I}"
  1404.     fi
  1405. done
  1406. if test "${MISSING}" = "" ; then
  1407.     echo You have unpacked all 14 archives.
  1408.     echo "See the readme file"
  1409.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1410. else
  1411.     echo You still need to unpack the following archives:
  1412.     echo "        " ${MISSING}
  1413. fi
  1414. ##  End of shell archive.
  1415. exit 0
  1416.